#pragma once

/**
 * @file mem_pool.h
 * @brief 内存池实现
 * @details 提供高效的对象内存分配和回收机制，减少内存碎片和提高性能
 */

#include <cstdint>
#include <vector>
#include <string>

#include "macros.h"

namespace Common {
  /**
   * @namespace Common
   * @brief 包含常用工具类和函数的命名空间
   */
  /**
   * @brief 内存池类模板
   * @tparam T 内存池管理的对象类型
   * @details 提供高效的对象分配和回收机制，避免频繁的堆内存分配和释放
   */
  template<typename T>
  class MemPool final {
  public:
    /**
     * @brief 构造函数
     * @param num_elems 内存池中预分配的对象数量
     * @details 初始化内存池并预分配指定数量的对象空间
     */
    explicit MemPool(std::size_t num_elems) :
        store_(num_elems, {T(), true}) /* pre-allocation of vector storage. */ {
      ASSERT(reinterpret_cast<const ObjectBlock *>(&(store_[0].object_)) == &(store_[0]), "T object should be first member of ObjectBlock.");
    }

    /**
     * @brief 分配一个新的T类型对象
     * @tparam Args 构造函数参数类型包
     * @param args 传递给T类型构造函数的参数
     * @return 返回指向新分配对象的指针
     * @details 使用placement new初始化对象，将块标记为已使用并返回对象指针
     */
    template<typename... Args>
    T *allocate(Args... args) noexcept {
      auto obj_block = &(store_[next_free_index_]);
      ASSERT(obj_block->is_free_, "Expected free ObjectBlock at index:" + std::to_string(next_free_index_));
      T *ret = &(obj_block->object_);
      ret = new(ret) T(args...); // placement new.
      obj_block->is_free_ = false;

      updateNextFreeIndex();

      return ret;
    }

    /**
     * @brief 将对象返回到内存池
     * @param elem 要释放的对象指针
     * @details 通过将块标记为空闲来返回对象。注意：对象的析构函数不会被调用
     */
    auto deallocate(const T *elem) noexcept {
      const auto elem_index = (reinterpret_cast<const ObjectBlock *>(elem) - &store_[0]);
      ASSERT(elem_index >= 0 && static_cast<size_t>(elem_index) < store_.size(), "Element being deallocated does not belong to this Memory pool.");
      ASSERT(!store_[elem_index].is_free_, "Expected in-use ObjectBlock at index:" + std::to_string(elem_index));
      store_[elem_index].is_free_ = true;
    }

    /**
     * @brief 删除的默认、拷贝和移动构造函数及赋值运算符
     * @details 防止意外的内存池复制或移动，确保内存池的唯一性
     */
    MemPool() = delete;

    MemPool(const MemPool &) = delete;

    MemPool(const MemPool &&) = delete;

    MemPool &operator=(const MemPool &) = delete;

    MemPool &operator=(const MemPool &&) = delete;

  private:
    /**
     * @brief 查找下一个可用的空闲块
     * @details 更新next_free_index_以指向下一个可用于分配的空闲块
     */
    auto updateNextFreeIndex() noexcept {
      const auto initial_free_index = next_free_index_;
      while (!store_[next_free_index_].is_free_) {
        ++next_free_index_;
        if (UNLIKELY(next_free_index_ == store_.size())) { // hardware branch predictor should almost always predict this to be false any ways.
          next_free_index_ = 0;
        }
        if (UNLIKELY(initial_free_index == next_free_index_)) {
          ASSERT(initial_free_index != next_free_index_, "Memory Pool out of space.");
        }
      }
    }

    /**
     * @brief 对象块结构
     * @details 将对象和其状态存储在一起可以提高缓存性能
     *          使用一个包含两个成员的结构体向量比使用两个单成员向量更好
     */
    struct ObjectBlock {
      T object_;
      bool is_free_ = true;
    };

    /**
     * @brief 存储对象块的向量
     * @details 使用std::vector而非std::array，因为当池大小增加时，堆内存比栈内存更适合
     *          尽管栈内存速度更快，但当池大小增加时性能会下降
     */
    std::vector<ObjectBlock> store_;

    size_t next_free_index_ = 0;
  };
}
